Skip to content

test(cov): coverage drivers for never-measured surfaces (+ the 85% branch ceiling, measured) - #147

Merged
gburd merged 2 commits into
masterfrom
test/coverage-85
Sep 7, 2026
Merged

test(cov): coverage drivers for never-measured surfaces (+ the 85% branch ceiling, measured)#147
gburd merged 2 commits into
masterfrom
test/coverage-85

Conversation

@gburd

@gburd gburd commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Phase C of the evolved plan. Adds test drivers for surfaces the suite never measured, and — importantly — establishes with arithmetic that >85% branch is not a reachable target, while >85% function is.

The honest headline: 85% branch is out of reach; 85% function is not

Current (committed lcov, report #3): line 68.0%, branch 50.2% (39238/78236), function 80.5% (2129/2644).

Reaching 85% branch needs +27,262 branches = 70% of every currently-missing branch. The structural reason: ~1,662 if ((ret = f()) != 0) sites in src/ are OOM/error-return legs reachable only by malloc-fault injection, and fi_sweep.c reaches exactly one failure point per process run (it sweeps K=1..M) — so covering K error legs costs K runs. Another 2,368 are NULL checks and 1,496 flag tests.

>85% function is the reachable goal: it needs only +118 functions, and ~250 recoverable ones were identified.

Drivers added (each validated with real check counts)

Artifact Result Targets
cov_api_surface.c 418 checks, 0 fail ~60 never-called getters (db_method.c 24, env_method.c 21, mp_fmethod.c 8, db_cds.c 8)
cov_rep_api.c 254 checks, 0 fail rep/repmgr config+query surface, DB_SITE, txn_applied, base-API↔repmgr cross-rejection
cov_logrec_print.c 5115 checks, 0 fail, 41 log-record types ~39 *_autop.c printers, 62 of log_verify_int.c's 99 functions
cov_codecs.c 290326 checks, 0 fail db_compint.c's 66 missing branches, db_getlong/getulong
cov_oom_paths.c builds with/without faultinject error-return branches; calls __gcov_dump() before _exit
7 runner scripts cutest 8/9 suites, fuzz 3 harnesses/14 inputs, DST skips cleanly tiers whose coverage was never collected

Two measurement gaps found (why some tiers showed as 0%)

  • fi_sweep.c's 947 fault points measured as zero because the child _exit()s without __gcov_dump(). Any driver that _exit()s must dump first or its coverage is invisible.
  • test/fuzz/run.sh builds its own ASan libdb, so corrupt-input rejection branches never reach the main profile.

Five latent defects found (reported, not fixed here)

  1. NULL-deref __lock_set_lk_priority/get_lk_priority (lock_method.c:482,507) — __lock_getlocker(create=0) returns 0 with *retp=NULL, both deref unconditionally; __lock_vec_pp (lock.c:93) shares the pattern.
  2. NULL-deref rep_get_nsites on an unopened repmgr handle (12-line repro; ENV_NOT_CONFIGURED is a no-op pre-open).
  3. __cdsgroup_begin installs 8 of DB_TXN's 12 methods — get_priority/set_priority/set_commit_token/set_txn_lsnp are NULL; calling them jumps to 0x0.
  4. TestQueue.c:64 buffer overrun in sh_l_as_string (harness bug, crashes at -O2).
  5. db_printlog in this fork has no -a flag (report 6.0.19 is missing #3's driver assumed it did).

Honest limitation

No new measured before/after numbers. A full coverage run needs a big box (report #3 used a 96-way instance); on this 8-core box shared with concurrent agents (load 143–216) the BEFORE run did not finish. baseline.txt is therefore untouched and report #4 is unwritten — the drivers are validated by their own check counts, not yet by a coverage delta. Finishing that is mechanical: full_run4.sh accumulates .gcda so the AFTER run reuses the BEFORE work.

Two driver assertions were corrected rather than filed as bugs: the compressed-int encoding is deliberately not order-preserving (only used for lengths/sizes, never as a sort key), and __db_getlong uses base-10 strtol, so rejecting "0x10" is correct.

cov-agent and others added 2 commits September 6, 2026 20:30
Four new C drivers plus six runner scripts aimed at the two largest
recoverable slices of the coverage gap documented in
test/coverage/FULL-COVERAGE-REPORT-3.md: the 515 never-called functions,
and the OOM error-return branches.

New drivers (test/c/cov_*.c, all single-process, SIGALRM-guarded):

* cov_api_surface.c (418 checks) -- the DB_ENV / DB / DBC / DB_TXN /
  DB_MPOOLFILE getter + callback-setter surface and the
  argument-validation / ENV_ILLEGAL_AFTER_OPEN halves of the setters.
  Targets the ~60 never-called getters in db_method.c (24),
  env_method.c (21), mp_fmethod.c (8) and db_cds.c (8): the Tcl
  bindings expose the setters a workload needs and never read a knob
  back, so every getter measured cold.

* cov_rep_api.c (254 checks) -- the replication + repmgr configuration
  and query surface: rep_get_config/limit/nsites/priority/request/
  timeout/clockskew, rep_set_clockskew/request/nsites, txn_applied, the
  DB_SITE handle methods, repmgr_get_ack_policy/config/local_site/
  site_by_eid, the channel *_inval stubs, and the base-API-vs-repmgr
  cross-rejection branches no Tcl rep test reaches (each picks one API
  and stays).

* cov_logrec_print.c (5115 checks) -- generates a log holding 41
  DISTINCT record kinds (hash contract/metagroup, queue extent
  create+delete, page truncation, compaction realloc, heap ops,
  off-page dups, cursor adjust under a child txn, subdb create,
  transactional rename/remove, DIAGNOSTIC, a prepared txn), then walks
  it through BOTH per-record dispatch tables: db_printlog
  (the ~39 *_autop.c printers) and db_log_verify (62 of
  log_verify_int.c's 99 functions -- the largest never-called count in
  the tree). One workload, two cold surfaces.

* cov_oom_paths.c -- a COVERAGE-oriented OOM sweep. fi_sweep.c is the
  bug-finding sweep and its children _exit(), which skips gcov's atexit
  flush, so its 947 failure points contribute ZERO measured coverage.
  This sweeps the same __os_* seam with a breadth-first workload and
  calls __gcov_dump() in each child before _exit. 6,016 of report #3's
  38,998 missing branches sit on `if ((ret = f()) != 0)` lines.
  Requires --enable-faultinject; SKIPs cleanly without it.

New runners for tiers that already existed and passed but that no
coverage driver had ever run, so what they cover always measured cold:

* run_cov_cutest.sh -- the 12-suite CuTest binary, one suite per
  process. TestChannel brings up three live repmgr sites and drives the
  whole DB_CHANNEL API, which is exactly repmgr_method.c's
  never-called set.
* run_cov_fuzz_corpus.sh -- the committed corpus + crash seeds replayed
  through FUZZ_STANDALONE harnesses against THIS build (run.sh and
  check-crashes.sh build their own ASan libdb, so the corrupt-input
  rejection branches were never counted).
* run_cov_dst.sh -- the 41 DST scenarios; SKIPs when the library lacks
  --enable-dst.

test/coverage/full_run4.sh is a portable rework of
full_run3_combined.sh: repo root derived from the script location,
tcl/gcov/lcov from the environment, PHASE 1 throttled to $COV_PAR so
the run survives an 8-way box, --enable-dst + --enable-faultinject added
(both additive and inert until armed), and PHASE 2 folds in
recd_handlers (which report #3 recorded as a measurement gap) plus all
the tiers above. SKIP_PHASE1/SKIP_PHASE2/PHASE2_ONLY_NEW allow adding
to an existing .gcda accumulation, which is how per-batch deltas are
measured.

Measurement only -- no engine source touched. Five engine/harness
findings surfaced while validating these drivers and are written up in
the report, not fixed here.
The driver did not compile: it called __db_compress_int/__db_decompress_int/
__db_decompress_int32 with a trailing length out-parameter, but all three RETURN
the byte count instead (see bt_compress.c: 'ptr += __db_compress_int(ptr, v)').
Fixed the three call sites.

It then ran with 12 failures, all of which were the driver asserting contracts
the code never promised -- verified against the source rather than filed as bugs:

- 8 x 'encoding is not order-preserving' at the size-class boundaries. The
  compressed-int encoding is deliberately NOT order-preserving, and does not
  need to be: its only in-tree users are bt_compress.c's prefix/suffix lengths
  and data sizes, which are decoded numerically and never compared as encoded
  byte strings. Assertion removed, with a comment recording why.
- 4 x '__db_getlong("0x10") rejected, expected accept'. __db_getlong and
  __db_getulong call strtol/strtoul with base 10 explicitly, so rejecting hex is
  correct. Cases now expect rejection; noted that "010" is decimal ten.

cov_codecs: 290326 checks, 0 failures.
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

ABI diff produced no report (build skipped or no base tag).


Advisory: libabigail/nm is the authoritative binary-ABI check; Coccinelle is complementary source-level early warning. See dist/cocci/README.md.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Coccinelle convention checks

No new violations. ✅

Resolved since baseline (2) -- update dist/cocci/baseline.txt to lock these in.
rule_mutex_unbalanced|MUTEX_UNBALANCED|src/crypto/mersenne/mt19937db.c|return (ret);
rule_mutex_unbalanced|MUTEX_UNBALANCED|src/mp/mp_register.c|return (ret);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant